﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace SocialKit.LightRest.OAuth
{
    /// <summary>
    /// Represents the abstract OAuth Token.
    /// </summary>
    [Serializable]
    public abstract class TokenBase
    {
        /// <summary>
        /// Gets or sets the token.
        /// </summary>
        public string Token { get; set; }

        /// <summary>
        /// Gets or sets the token secret.
        /// </summary>
        public string Secret { get; set; }

        /// <summary>
        /// Initialize an instance class that inherited from SocialKit.LightOAuth.TokenBase.
        /// </summary>
        /// <param name="token">The token value.</param>
        /// <param name="secret">The token secret value.</param>
        public TokenBase(string token, string secret)
        {
            this.Token = token;
            this.Secret = secret;
        }

        /// <summary>
        /// Returns the normalized format of the current token.
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return String.Format("{0}={1}&{2}={3}", OAuthStrings.OAuthTokenKey, this.Token, OAuthStrings.OAuthTokenSecretKey, this.Secret);
        }
    }

    /// <summary>
    /// Represents the token of a consumer.
    /// </summary>
    [Serializable]
    public abstract class ConsumerToken : TokenBase
    {
        /// <summary>
        /// Gets the consumer info.
        /// </summary>
        public Consumer Consumer { get; private set; }

        /// <summary>
        /// Initialize a new instance of SocialKit.LightOAuth.ConsumerToken class.
        /// </summary>
        /// <param name="consumer">The consumer.</param>
        /// <param name="token">The token value.</param>
        /// <param name="secret">The token secret value.</param>
        public ConsumerToken(Consumer consumer, string token, string secret)
            : base(token, secret)
        {
            this.Consumer = consumer;
        }
    }

    /// <summary>
    /// Represents the Request Token of OAuth.
    /// </summary>
    [Serializable]
    public class RequestToken : ConsumerToken
    {
        IDictionary<string, string> allTokenValues;

        /// <summary>
        /// Gets a dictionary indicates all values responed the oauth server.
        /// </summary>
        public IDictionary<string, string> AllTokenValues
        {
            get
            {
                if (this.allTokenValues == null)
                    this.allTokenValues = new Dictionary<string, string>();

                return this.allTokenValues;
            }
        }


        /// <summary>
        /// Initialize a new instance of SocialKit.LightOAuth.RequestToken class.
        /// </summary>
        /// <param name="consumer">The consumer.</param>
        /// <param name="token">The token value.</param>
        /// <param name="secret">The token secret value.</param>
        /// <param name="allTokenValues">All values.</param>
        public RequestToken(Consumer consumer, string token, string secret, IDictionary<string, string> allTokenValues)
            : base(consumer, token, secret)
        {
            this.allTokenValues = allTokenValues;
        }


        /// <summary>
        /// Gets a string indicates the normalized authorize uri.
        /// </summary>
        /// <param name="additionalParameters">The additional parameters normalized as URI query string(like a=1&amp;b=2).</param>
        /// <returns></returns>
        public string GetNormalizedAuthorizeUri(string additionalParameters)
        {
            return GetNormalizedAuthorizeUri(RestUtility.ParseQueryString(additionalParameters).ToArray());
        }

        /// <summary>
        /// Gets a string indicates the normalized authorize uri.
        /// </summary>
        /// <param name="additionalParameters">The additional parameters defined by the service provider used to authorize the user.</param>
        /// <returns></returns>
        public string GetNormalizedAuthorizeUri(params KeyValuePair<string, string>[] additionalParameters)
        {
            return new HttpQueryString(additionalParameters)
                .Append(OAuthStrings.OAuthTokenKey, this.Token)
                .MakeQueryString(new Uri(this.Consumer.AuthorizeUri))
                .ToString();
        }

        /// <summary>
        /// Convert this authorized request token to a new access token.
        /// </summary>
        /// <param name="verifier">The verifier value.</param>
        /// <param name="additionalParameters">The additional parameters normalized as URI query string(like a=1&amp;b=2).</param>
        /// <returns></returns>
        public AccessToken ToAccessToken(string verifier, string additionalParameters)
        {
            return ToAccessToken(verifier, RestUtility.ParseQueryString(additionalParameters).ToArray());
        }

        /// <summary>
        /// Convert this authorized request token to a new access token.
        /// </summary>
        /// <param name="verifier">The verifier value.</param>
        /// <param name="additionalParameters">The additional parameters.</param>
        /// <returns></returns>
        public AccessToken ToAccessToken(string verifier, params KeyValuePair<string, string>[] additionalParameters)
        {
            if (verifier == null)
                verifier = string.Empty;

            var values = additionalParameters.ToList();
            values.Add(new KeyValuePair<string, string>(OAuthStrings.OAuthVerifierKey, verifier));

            var request = new OAuthHttpRequestMessage("POST", this.Consumer.AccessTokenUri)
                .Sign(this.Consumer, this.Token, this.Secret, values.ToArray());

            var responseText = new RestClient().Send(request).ReadContentAsString();

            var tokenValues = RestUtility.ParseQueryString(responseText)
                .ToDictionary(p => p.Key, p => p.Value);

            return new AccessToken(this.Consumer, tokenValues[OAuthStrings.OAuthTokenKey], tokenValues[OAuthStrings.OAuthTokenSecretKey], tokenValues);
        }
    }

    /// <summary>
    /// Represents the Access Token of OAuth.
    /// </summary>
    [Serializable]
    public class AccessToken : ConsumerToken
    {
        IDictionary<string, string> allTokenValues;

        /// <summary>
        /// Gets a dictionary indicates all values responed the oauth server.
        /// </summary>
        public IDictionary<string, string> AllTokenValues
        {
            get
            {
                if (this.allTokenValues == null)
                    this.allTokenValues = new Dictionary<string, string>();

                return this.allTokenValues;
            }
        }

        /// <summary>
        /// Initialize a new instance of SocialKit.LightOAuth.AccessToken class.
        /// </summary>
        /// <param name="consumer">The consumer.</param>
        /// <param name="token">The token value.</param>
        /// <param name="secret">The token secret value.</param>
        public AccessToken(Consumer consumer, string token, string secret)
            : this(consumer, token, secret, new Dictionary<string, string> 
            { 
                { OAuthStrings.OAuthTokenKey, token }, 
                { OAuthStrings.OAuthTokenSecretKey, secret } 
            })
        {
        }

        /// <summary>
        /// Initialize a new instance of SocialKit.LightOAuth.AccessToken class.
        /// </summary>
        /// <param name="consumer">The consumer.</param>
        /// <param name="token">The token value.</param>
        /// <param name="secret">The token secret value.</param>
        /// <param name="allTokenValues">All values.</param>
        public AccessToken(Consumer consumer, string token, string secret, IDictionary<string, string> allTokenValues)
            : base(consumer, token, secret)
        {
            this.allTokenValues = allTokenValues;
        }
    }
}